﻿/**
 * Copyright (c) 2008 Justin Etheredge http://www.codethinked.com
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Microsoft Public License (Ms-PL)
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/ms-pl.html
 * If redistributing this code, this entire header must remain intact.
 * 
 * Some of the code in this application has been taken 
 * from the following sources (all of which are licensed under
 * the Ms-PL):
 * Matt Warren's Blog: http://blogs.msdn.com/mattwar/
 * MSDN: http://msdn2.microsoft.com/en-us/library/bb546158.aspx
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace LinqToSimpleDB
{
  public class SimpleDBAttribute : IDirtyTrackable, IDeletable
  {
    private string _name = string.Empty;
    private string[] _values = null;
    private bool[] _isNumeric = null;
    private decimal[] _numericValues = null;

    private bool _isDirty = true;
    private bool _isDeleted = false;

    public SimpleDBAttribute()
    {
    }

    public SimpleDBAttribute(string name, params string[] values)
    {
      ((IDirtyTrackable)this).MarkDirty();
      this._name = name;
      this.Values = values;
    }

    public string Name
    {
      get { return _name; }
    }

    public string[] Values
    {
      get { return _values; }
      set
      {
        if (this._values != value)
        {
          ((IDirtyTrackable)this).MarkDirty();
        }
        _isNumeric = null;
        _numericValues = null;
        _values = value;
      }
    }

    internal bool[] Numeric
    {
      get
      {
        InitializeNumericValues();
        return _isNumeric;
      }
    }

    internal decimal[] NumericValues
    {
      get
      {
        InitializeNumericValues();
        return _numericValues;
      }
    }

    public bool IsNumeric
    {
      get
      {
        InitializeNumericValues();

        //if we have *any* numbers then we consider this numeric
        foreach (bool isNumeric in _isNumeric)
        {
          if (isNumeric)
          {
            return true;
          }
        }
        return false;
      }
    }

    private void InitializeNumericValues()
    {
      if (_numericValues == null)
      {
        _numericValues = new decimal[_values.Length];
        _isNumeric = new bool[_values.Length];

        for (int i = 0; i < _values.Length; i++)
        {
          _isNumeric[i] = Decimal.TryParse(_values[i], out _numericValues[i]);
        }
      }
    }

    public override bool Equals(object obj)
    {
      if (obj.GetType() == typeof(SimpleDBAttribute))
      {
        SimpleDBAttribute comparingObject = (SimpleDBAttribute)obj;
        if (this.Values.Length != comparingObject.Values.Length)
        {
          return false;
        }

        if (this.IsNumeric)
        {
          if (comparingObject.IsNumeric)
          {
            foreach (decimal numericValue in _numericValues)
            {
              if (!comparingObject.HasValue(numericValue))
              {
                return false;
              }
            }
          }
          else
          {
            return false;
          }
        }

        foreach (string stringValue in _values)
        {
          if (!comparingObject.HasValue(stringValue))
          {
            return false;
          }
        }
        return true;
      }
      else
      {
        return false;
      }
    }

    public override int GetHashCode()
    {
      string value = this.Name;
      foreach (string item in this.Values)
      {
        value += item;
      }
      return value.GetHashCode();
    }

    public SimpleDBAttribute WithValue(string value)
    {
      ((IDirtyTrackable)this).MarkDirty();
      string[] values = new string[this.Values.Length + 1];
      this.Values.CopyTo(values, 0);
      values[values.Length - 1] = value;
      this.Values = values;
      return this;
    }

    public void AddValue(string value)
    {
      this.WithValue(value);
    }

    #region Operators

    public static bool operator ==(string leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(s => String.Equals(leftOperand, s, StringComparison.CurrentCulture));
    }

    public static bool operator ==(SimpleDBAttribute leftOperand, string rightOperand)
    {
      return leftOperand.CheckAny(s => String.Equals(s, rightOperand, StringComparison.CurrentCulture));
    }

    public static bool operator ==(decimal leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand == n);
    }

    public static bool operator ==(SimpleDBAttribute leftOperand, decimal rightOperand)
    {
      return leftOperand.CheckAny(n => n == rightOperand);
    }

    public static bool operator ==(double leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand == n);
    }

    public static bool operator ==(SimpleDBAttribute leftOperand, double rightOperand)
    {
      return leftOperand.CheckAny(n => n == (decimal)rightOperand);
    }

    public static bool operator ==(int leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand == n);
    }

    public static bool operator ==(SimpleDBAttribute leftOperand, int rightOperand)
    {
      return leftOperand.CheckAny(n => n == (decimal)rightOperand);
    }

    public static bool operator ==(SimpleDBAttribute leftOperand, bool rightOperand)
    {
      return leftOperand.CheckAny(n => n == rightOperand, false, false);
    }

    public static bool operator ==(bool leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand == n, false, false);
    }

    public static bool operator !=(string leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(s => !(String.Equals(leftOperand, s, StringComparison.CurrentCulture)));
    }

    public static bool operator !=(SimpleDBAttribute leftOperand, string rightOperand)
    {
      return leftOperand.CheckAny(s => !(String.Equals(rightOperand, s, StringComparison.CurrentCulture)));
    }

    public static bool operator !=(decimal leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand != n);
    }

    public static bool operator !=(SimpleDBAttribute leftOperand, decimal rightOperand)
    {
      return leftOperand.CheckAny(n => n != rightOperand);
    }

    public static bool operator !=(double leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand != n);
    }

    public static bool operator !=(SimpleDBAttribute leftOperand, double rightOperand)
    {
      return leftOperand.CheckAny(n => n != (decimal)rightOperand);
    }

    public static bool operator !=(int leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand != n);
    }

    public static bool operator !=(SimpleDBAttribute leftOperand, int rightOperand)
    {
      return leftOperand.CheckAny(n => n != (decimal)rightOperand);
    }

    public static bool operator !=(SimpleDBAttribute leftOperand, bool rightOperand)
    {
      return leftOperand.CheckAny(n => n != rightOperand, false, true);
    }

    public static bool operator !=(bool leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand != n, false, true);
    }

    public static bool operator >(SimpleDBAttribute leftOperand, string rightOperand)
    {
      return leftOperand.CheckAny(s => String.Compare(s, rightOperand, StringComparison.CurrentCulture) > 0);
    }

    public static bool operator >(string leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(s => String.Compare(leftOperand, s, StringComparison.CurrentCulture) > 0);
    }

    public static bool operator <(SimpleDBAttribute leftOperand, string rightOperand)
    {
      return leftOperand.CheckAny(s => String.Compare(s, rightOperand, StringComparison.CurrentCulture) < 0);
    }

    public static bool operator <(string leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(s => String.Compare(leftOperand, s, StringComparison.CurrentCulture) < 0);
    }

    public static bool operator >=(SimpleDBAttribute leftOperand, string rightOperand)
    {
      return leftOperand.CheckAny(s => String.Compare(s, rightOperand, StringComparison.CurrentCulture) >= 0);
    }

    public static bool operator >=(string leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(s => String.Compare(leftOperand, s, StringComparison.CurrentCulture) >= 0);
    }

    public static bool operator <=(SimpleDBAttribute leftOperand, string rightOperand)
    {
      return leftOperand.CheckAny(s => String.Compare(s, rightOperand, StringComparison.CurrentCulture) <= 0);
    }

    public static bool operator <=(string leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(s => String.Compare(leftOperand, s, StringComparison.CurrentCulture) <= 0);
    }

    public static bool operator >(SimpleDBAttribute leftOperand, decimal rightOperand)
    {
      return leftOperand.CheckAny(n => n > rightOperand);
    }

    public static bool operator >(decimal leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand > n);
    }

    public static bool operator <(SimpleDBAttribute leftOperand, decimal rightOperand)
    {
      return leftOperand.CheckAny(n => n < rightOperand);
    }

    public static bool operator <(decimal leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand < n);
    }

    public static bool operator >=(SimpleDBAttribute leftOperand, decimal rightOperand)
    {
      return leftOperand.CheckAny(n => n >= rightOperand);
    }

    public static bool operator >=(decimal leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand >= n);
    }

    public static bool operator <=(SimpleDBAttribute leftOperand, decimal rightOperand)
    {
      return leftOperand.CheckAny(n => n <= rightOperand);
    }

    public static bool operator <=(decimal leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => leftOperand <= n);
    }

    public static bool operator >(SimpleDBAttribute leftOperand, double rightOperand)
    {
      return leftOperand.CheckAny(n => n > (decimal)rightOperand);
    }

    public static bool operator >(double leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand > n);
    }

    public static bool operator <(SimpleDBAttribute leftOperand, double rightOperand)
    {
      return leftOperand.CheckAny(n => n < (decimal)rightOperand);
    }

    public static bool operator <(double leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand < n);
    }

    public static bool operator >=(SimpleDBAttribute leftOperand, double rightOperand)
    {
      return leftOperand.CheckAny(n => n >= (decimal)rightOperand);
    }

    public static bool operator >=(double leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand >= n);
    }

    public static bool operator <=(SimpleDBAttribute leftOperand, double rightOperand)
    {
      return leftOperand.CheckAny(n => n <= (decimal)rightOperand);
    }

    public static bool operator <=(double leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand <= n);
    }

    public static bool operator >(SimpleDBAttribute leftOperand, int rightOperand)
    {
      return leftOperand.CheckAny(n => n > (decimal)rightOperand);
    }

    public static bool operator >(int leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand > n);
    }

    public static bool operator <(SimpleDBAttribute leftOperand, int rightOperand)
    {
      return leftOperand.CheckAny(n => n < (decimal)rightOperand);
    }

    public static bool operator <(int leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand < n);
    }

    public static bool operator >=(SimpleDBAttribute leftOperand, int rightOperand)
    {
      return leftOperand.CheckAny(n => n >= (decimal)rightOperand);
    }

    public static bool operator >=(int leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand >= n);
    }

    public static bool operator <=(SimpleDBAttribute leftOperand, int rightOperand)
    {
      return leftOperand.CheckAny(n => n <= (decimal)rightOperand);
    }

    public static bool operator <=(int leftOperand, SimpleDBAttribute rightOperand)
    {
      return rightOperand.CheckAny(n => (decimal)leftOperand <= n);
    }

    #endregion

    public bool StartsWith(string value)
    {
      return this.CheckAny(n => n.StartsWith(value, StringComparison.CurrentCulture));
    }

    public bool HasValue(decimal value)
    {
      foreach (decimal num in _numericValues)
      {
        if (value == num)
        {
          return true;
        }
      }
      return false;
    }

    public bool HasValue(string value)
    {
      foreach (string str in _values)
      {
        if (value == str)
        {
          return true;
        }
      }
      return false;
    }

    public bool CheckAny(Func<string, bool> func)
    {
      foreach (string str in _values)
      {
        if (func(str))
        {
          return true;
        }
      }
      return false;
    }

    public bool CheckAny(Func<decimal, bool> func)
    {
      if (this.IsNumeric)
      {
        for (int i = 0; i < _numericValues.Length; i++)
        {
          if (_isNumeric[i])
          {
            if (func(_numericValues[i]))
            {
              return true;
            }
          }
        }
      }
      return false;
    }

    public bool CheckAny(Func<bool, bool> func, bool returnOnSome, bool returnOnNone)
    {
      bool anyFound = false;
      foreach (string str in _values)
      {
        bool val = false;
        if (str == "true")
        {
          anyFound = true;
          val = true;
          if (func(val))
          {
            return true;
          }
        }
        else if (str == "false")
        {
          anyFound = true;
          val = false;
          if (func(val))
          {
            return true;
          }
        }
      }
      return anyFound ? returnOnSome : returnOnNone;
    }

    #region IDirtyTrackable Members

    public bool IsDirty
    {
      get { return _isDirty; }
    }

    void LinqToSimpleDB.IDirtyTrackable.MarkClean()
    {
      _isDirty = false;
    }

    void LinqToSimpleDB.IDirtyTrackable.MarkDirty()
    {
      _isDirty = true;
    }

    #endregion

    #region IDeletable Members

    bool LinqToSimpleDB.IDeletable.IsDeleted
    {
      get { return _isDeleted; }
    }

    void LinqToSimpleDB.IDeletable.MarkDeleted()
    {
      _isDeleted = true;
    }

    public void Delete()
    {
      ((IDeletable)this).MarkDeleted();
    }

    #endregion
  }
}
